﻿// ===============================================================================
// InputControl.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Mvc.Controls
{
    using System;
    using EOS.Framework.Rules;
    using EOS.Framework.Tools;
    using EOS.Framework.Tools.Aspects;

    /// <summary>
    /// Abstract base class for all input controls.
    /// </summary>
    public abstract class InputControl<TValue> : Control, IValidatedInputControl
    {
        private IForm _form;

        #region Properties

        /// <summary>
        /// Indicates if the input value is valid or not.
        /// </summary>
        private bool _isValid = true;

        /// <summary>
        /// Gets or sets a value indicating whether this instance is valid.
        /// </summary>
        /// <value><c>true</c> if this instance is valid; otherwise, <c>false</c>.</value>
        public bool IsValid
        {
            get { return _isValid; }
        }

        /// <summary>
        /// Gets or sets the name of the input control.
        /// </summary>
        private string _name;

        /// <summary>
        /// Gets or sets the name of the input control.
        /// </summary>
        /// <value>The name of the input control. Cannot be null or empty or cannot have only whitespaces.</value>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="name"/> is a string of length 0 or 
        /// contains only whitespaces.</exception>
        public string Name
        {
            get
            {
                if (string.IsNullOrEmpty(_name))
                {
                    return Key;
                }
                else
                {
                    return _name;
                } 
            }
            set 
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }

                if (StringHelper.IsEmptyOrBlanksOnly(value))
                {
                    throw new ArgumentException(Properties.Resources.EmptyStringParameter, "value");
                }

                _name = value; 
            }
        }

        /// <summary>
        /// The error message, if the value is not valid.
        /// </summary>
        private string _errorMessage;

        /// <summary>
        /// Gets or sets the error message.
        /// </summary>
        /// <value>The error message.</value>
        /// <remarks>Set ErrorMessage to null, if you dont need a error message anymore.</remarks>
        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; }
        }

        /// <summary>
        /// The value.
        /// </summary>
        private string _inputValue;

        /// <summary>
        /// Gets or sets the incoming value.
        /// </summary>
        /// <value>The value.</value>
        /// <remarks>Set value to null to clear the value.</remarks>
        public string InputValue
        {
            get { return _inputValue; }
            set { _inputValue = value; }
        }

        /// <summary>
        /// The converted value.
        /// </summary>
        private TValue _value;

        /// <summary>
        /// Gets the the converted value.
        /// </summary>
        /// <value>The converted value.</value>
        public TValue Value
        {
            get { return _value; }
        }

        /// <summary>
        /// A collection of rules.
        /// </summary>
        private RulesCollection _rules = new RulesCollection();

        /// <summary>
        /// Gets or sets the rules, which are responsible for validing the value.
        /// </summary>
        /// <value>The rules.</value>
        public RulesCollection Rules
        {
            get { return _rules; }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="InputControl&lt;TValue&gt;"/> class.
        /// </summary>
        [ParamNotNull("form")]
        protected InputControl(IForm form)
        {
            _form = form;
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="InputControl&lt;TValue&gt;"/> class
        /// with the specified name.
        /// </summary>
        /// <param name="name">The name. Cannot be null or empty or contains only blanks.</param>        
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="name"/> is a string of length 0 or 
        /// contains only whitespaces.</exception>
        [ParamNotNull("form")]
        protected InputControl(IForm form, string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            if (StringHelper.IsEmptyOrBlanksOnly(name))
            {
                throw new ArgumentException(Properties.Resources.EmptyStringParameter, "name");
            }

            this._form = form;
            this._name = name;
        }

        protected override void WriteBeginAttachedTag(IXhtmlWriter writer)
        {
            if (string.IsNullOrEmpty(_name))
            {
                base.WriteBeginAttachedTag(writer);
            }
            else
            {
                base.WriteBeginAttachedTag(writer, new string[] { "name" });
                writer.WriteAttribute("name", _name);
            }
        }

        protected override void WriteBeginAttachedTag(IXhtmlWriter writer, string[] attributeFilter)
        {
            if (string.IsNullOrEmpty(_name))
            {
                base.WriteBeginAttachedTag(writer, attributeFilter);
            }
            else
            {
                string[] filter = new string[attributeFilter.Length + 1];
                for (int i = 0; i < attributeFilter.Length; i++)
                {
                    filter[i] = attributeFilter[i];
                }
                filter[attributeFilter.Length] = "name";

                base.WriteBeginAttachedTag(writer, filter);
                writer.WriteAttribute("name", _name);
            }
        }

        #region IInputControl Members

        /// <summary>
        /// Resets this instance.
        /// </summary>
        public void Reset()
        {
            this._value = default(TValue);
            this._inputValue = null;
            this._errorMessage = null;
            this._rules.Clear();
        }

        #endregion

        protected override void OnAttached()
        {
            if (_inputValue == null && _form != null)
            {
                _inputValue = _form.GetValue(Name);
            }

            if (_inputValue != null)
            {
                string message = string.Empty;

                _isValid = true;
                foreach (Rule rule in Rules)
                {
                    if (!rule.Validate(_inputValue))
                    {
                        _isValid = false;
                        message += rule.Description + " ";
                    }
                }
                _errorMessage = message.Trim();

                if (_isValid)
                {
                    try
                    {
                        this._value = (TValue)Injection.ChangeType(typeof(TValue), _inputValue);
                    }
                    catch (InvalidCastException)
                    {
                        _isValid = false;
                    }
                }
            }

            base.OnAttached();
        }
    }
}
